package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.album.AlbumStatVo;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.RequestBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;
    @Autowired
    private Executor threadPoolTaskExecutor;
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    /**
     * 站内专辑检索
     *
     * @param albumIndexQuery 查询条件
     * @retrun albumSearchResponseVo 返回Vo视图对象
     */
    @SneakyThrows
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        //查询语句
        //1 构建请求体
        SearchRequest searchRequest = this.buildDSL4Search(albumIndexQuery);
        System.err.println(searchRequest);
        //2 执行查询
        SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        //3 解析响应结果,封装响应对象
        AlbumSearchResponseVo albumSearchResponseVo = this.handleResponse4Search(searchResponse, albumIndexQuery);
        return albumSearchResponseVo;
    }

    /**
     * 查询指定一级分类下的热门专辑排行
     *
     * @param category1Id 一级分类ID
     * @return mapList 三级分类map与热度top6专辑集合
     */
    @Override
    public List<Map<String, Object>> channel(Long category1Id) {
        try {
            //获取一级分类下七个指定三级分类 通过远程调用接口
            List<BaseCategory3> baseCategory3Top7List = albumFeignClient.findTopBaseCategory3(category1Id).getData();
            Assert.notNull(baseCategory3Top7List, "没有查询到该分类列表");
            //map封装信息: baseCategory3:三级分类对象 ; list:包含装机集合limit 6
            Map<Long, BaseCategory3> category3Map = baseCategory3Top7List.stream().collect(Collectors.toMap(BaseCategory3::getId, category3 -> category3));
            //检索ES获取每个指定分类及其包含专辑热度前六专辑列表
            List<FieldValue> baseCategory3IdList = baseCategory3Top7List.stream().map(category3 -> FieldValue.of(category3.getId())).collect(Collectors.toList());
            SearchRequest searchRequest = this.buildDSL4Channel(baseCategory3IdList);
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
            //处理ES检索结果,封装专辑数据
            List<Map<String, Object>> mapList = this.handleResponse4Channel(searchResponse, category3Map);
            return mapList;
        } catch (IOException e) {
            log.error("ES检索异常：{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }


    /**
     * 构建DSL语句
     *
     * @param albumIndexQuery 请求参数
     * @return saerchaRequest 请求体
     */
    @Override
    public SearchRequest buildDSL4Search(AlbumIndexQuery albumIndexQuery) {
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        //添加索引库
        searchRequestBuilder.index("albuminfo");
        //添加查询参数: 关键字,三级分类,标签
        //关键字条件
        String keyword = albumIndexQuery.getKeyword();
        if (StringUtils.isNotEmpty(keyword)) {
            searchRequestBuilder.query(q -> q.bool(b -> b.must(
                    m -> m.match(ma -> ma.field("albumTitle").query(keyword))
            )));
            //添加高亮展示
            searchRequestBuilder.highlight(
                    h -> h.fields("albumTitle", h1 -> h1.preTags(
                            "<font style='color:red'>").postTags("</font>"))
            );
        }
        //分类条件
        Long category1Id = albumIndexQuery.getCategory1Id();
        if (category1Id != null) {
            searchRequestBuilder.query(q -> q.bool(b -> b.filter(f -> f.term(t -> t.field(
                    "category1Id").value(category1Id)))));
        }
        Long category2Id = albumIndexQuery.getCategory2Id();
        if (category2Id != null) {
            searchRequestBuilder.query(q -> q.bool(b -> b.filter(f -> f.term(t -> t.field(
                    "category2Id").value(category2Id)))));
        }
        Long category3Id = albumIndexQuery.getCategory3Id();
        if (category3Id != null) {
            searchRequestBuilder.query(q -> q.bool(b -> b.filter(f -> f.term(t -> t.field(
                    "category3Id").value(category3Id)))));
        }
        //标签条件
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (CollUtil.isNotEmpty(attributeList)) {
            for (String attribute : attributeList) {
                String[] attributeSplit = attribute.split(":");
                if (attributeSplit != null) {
                    searchRequestBuilder.query(q -> q.bool(b -> b.filter(f -> f.nested(
                            n -> n.path("attributeValueIndexList").query(
                                    q1 -> q1.bool(b1 -> b1.must(mAttribute -> mAttribute.term(
                                                    t -> t.field("attributeValueIndexList.attributeId").value(attributeSplit[0])))
                                            .must(mValue -> mValue.term(t2 -> t2.field("attributeValueIndexList.calueId").value(attributeSplit[1]))))
                            ))
                    )));
                }
            }
        }
        //添加分页参数
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        int from = (pageNo - 1) * pageSize;
        searchRequestBuilder.from(from).size(pageSize);
        //添加排序参数
        String queryOrder = albumIndexQuery.getOrder();
        if (StringUtils.isNotEmpty(queryOrder)) {
            String[] orderSplit = queryOrder.split(":");
            if (orderSplit != null && orderSplit.length == 2) {
                String orderFeild = "";
                switch (orderSplit[0]) {
                    case "1":
                        orderFeild = "hotScore";
                        break;
                    case "2":
                        orderFeild = "playStatNum";
                        break;
                    case "3":
                        orderFeild = "createTime";
                        break;
                }
                SortOrder sortOrder = orderSplit[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                String finalOrderFeild = orderFeild;
                searchRequestBuilder.sort(
                        s -> s.field(
                                f -> f.field(
                                        finalOrderFeild).order(sortOrder)));
            }
        }
        //添加指定字段
        searchRequestBuilder.source(
                s -> s.filter(
                        f -> f.excludes(
                                "attributeValueIndexList",
                                "hotScore",
                                "commentStatNum",
                                "buyStatNum",
                                "subscribeStatNum",
                                "announcerName")));
        return searchRequestBuilder.build();
    }

    /**
     * 解析查询结果
     *
     * @param searchResponse  查询结果
     * @param albumIndexQuery 请求条件
     * @return albumSearchResponseVo 返回视图对象
     */
    @Override
    public AlbumSearchResponseVo handleResponse4Search(SearchResponse<AlbumInfoIndex> searchResponse,
                                                       AlbumIndexQuery albumIndexQuery) {
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        //封装分页信息
        long total = searchResponse.hits().total().value();
        albumSearchResponseVo.setTotal(total);
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        Integer pageSize = albumIndexQuery.getPageSize();
        albumSearchResponseVo.setPageSize(pageSize);
        Long totalPages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        albumSearchResponseVo.setTotalPages(totalPages);

        //封装检索视图对象集合
        List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
        if (CollUtil.isNotEmpty(hits)) {
            List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {
                //获取专辑信息
                AlbumInfoIndex albumInfoIndex = hit.source();
                //处理高亮关键字
                Map<String, List<String>> highlight = hit.highlight();
                if (CollUtil.isNotEmpty(highlight)) {
                    albumInfoIndex.setAlbumTitle(highlight.get("albumTitle").get(0));
                }
                return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
            }).collect(Collectors.toList());
            albumSearchResponseVo.setList(albumInfoIndexVoList);
        }
        return albumSearchResponseVo;
    }


    /**
     * 构建channel请求DSL请求体
     *
     * @param baseCategory3IdList 三级分类Id集合
     * @return SearchRequest 请求体
     */
    @Override
    public SearchRequest buildDSL4Channel(List<FieldValue> baseCategory3IdList) {
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index("albuminfo");
        searchRequestBuilder.query(q -> q.terms(ts -> ts
                .field("category3Id")
                .terms(ts1 -> ts1.value(baseCategory3IdList))));
        searchRequestBuilder.aggregations("category3_aggs", aggs -> aggs.terms(ts -> ts.field("category3Id").size(7))
                .aggregations("albuminfo_top6", aggs1 -> aggs1
                        .topHits(th -> th
                                .sort(s -> s.field(f -> f.field("hotScore").order(SortOrder.Desc)))
                                .size(6)
                                .source(sc -> sc.filter(f -> f.excludes(
                                                        "attributeValueIndexList",
                                                        "hotScore",
                                                        "commentStatNum",
                                                        "buyStatNum",
                                                        "subscribeStatNum",
                                                        "announcerName"
                                                )
                                        )
                                )
                        )
                )
        );
        searchRequestBuilder.size(0);
        return searchRequestBuilder.build();
    }

    /**
     * 处理channel查询结果
     *
     * @param searchResponse 查询结果
     * @return listMap
     */
    @Override
    public List<Map<String, Object>> handleResponse4Channel(SearchResponse<AlbumInfoIndex> searchResponse,
                                                            Map<Long, BaseCategory3> category3Map) {
        Assert.notNull(searchResponse, "响应结果为空");
        List<LongTermsBucket> bucketList = searchResponse.aggregations().get("category3_aggs").lterms().buckets().array();
        List<Map<String, Object>> mapList = bucketList.stream().map(bucket -> {
                    HashMap<String, Object> map = new HashMap<>();
                    //存入baseCategory3
                    map.put("baseCategory3", category3Map.get(bucket.key()));
                    //存入list
                    List<AlbumInfoIndex> albuminfoTop6 = bucket.aggregations().get("albuminfo_top6").topHits().hits().hits().stream().map(hit -> {
                        return JSON.parseObject(hit.source().toString(), AlbumInfoIndex.class);
                    }).collect(Collectors.toList());
                    map.put("list", albuminfoTop6);
                    return map;
                }
        ).collect(Collectors.toList());
        return mapList;
    }

    /**
     * 保存专辑标题索引至索引库,用于关键字补全功能
     *
     * @param albumInfoIndex
     */
    @Override
    public void saveSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        //创建标题文档
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        //原始内容
        String albumTitle = albumInfoIndex.getAlbumTitle();
        suggestIndex.setTitle(albumTitle);
        //用于汉字自动补全字段
        suggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
        //用于汉语拼音自动补全字段
        String pinyin = PinyinUtil.getPinyin(albumTitle, "");
        suggestIndex.setKeywordPinyin(new Completion(new String[]{pinyin}));
        //用于汉语拼音首字母自动补全字段
        String firstLetter = PinyinUtil.getFirstLetter(albumTitle, "");
        suggestIndex.setKeywordSequence(new Completion(new String[]{firstLetter}));
        suggestIndexRepository.save(suggestIndex);
    }

    /**
     * 根据用户录入关键字查询索引库自动匹配补全关键字
     *
     * @param keyword 用户输入关键字
     * @return keywordList 返回完整字符串集合
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            //优先检索suggest索引库文档,不足展示要求10条内容时,检索albuminfo索引库文档补齐
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(s -> s.index("suggestinfo").suggest(suggest -> suggest
                    .suggesters("keywordSuggester", suggester -> suggester.completion(c -> c.field("keyword").skipDuplicates(true)).prefix(keyword))
                    .suggesters("pinyinSuggester", suggester -> suggester.completion(c -> c.field("keywordPinyin").skipDuplicates(true)).prefix(keyword))
                    .suggesters("sequenceSuggester", suggester -> suggester.completion(c -> c.field("keywordSequence").skipDuplicates(true)).prefix(keyword))), SuggestIndex.class);
            List<String> keywordSuggesterList = this.handleResponse4CompleteSuggest(searchResponse, "keywordSuggester");
            List<String> pinyinSuggesterList = this.handleResponse4CompleteSuggest(searchResponse, "pinyinSuggester");
            List<String> sequenceSuggesterList = this.handleResponse4CompleteSuggest(searchResponse, "sequenceSuggester");
            HashSet<String> hashSet = new HashSet<>();
            hashSet.addAll(keywordSuggesterList);
            hashSet.addAll(pinyinSuggesterList);
            hashSet.addAll(sequenceSuggesterList);

            //下拉框展示标题数为10,若set数量不够,检索albuminfo索引库补齐
            if (hashSet.size() < 10) {
                SearchResponse<AlbumInfoIndex> albumInfoIndexSearchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                                .query(q -> q.match(m -> m.field("albumTitle").query(keyword)))
                                .source(sc -> sc.filter(f -> f.includes("albumTitle")))
                                .size(10)
                        , AlbumInfoIndex.class);
                List<Hit<AlbumInfoIndex>> hitList = albumInfoIndexSearchResponse.hits().hits();
                if (CollUtil.isNotEmpty(hitList)) {
                    for (Hit<AlbumInfoIndex> hit : hitList) {
                        String albumTitle = hit.source().getAlbumTitle();
                        hashSet.add(albumTitle);
                        if (hashSet.size() >= 10) {
                            break;
                        }
                    }
                }
            }
            //补全内容超过10条,截选1-10条
            if (hashSet.size() >= 10) {
                return new ArrayList<>(hashSet).subList(0, 10);
            }
            return new ArrayList<>(hashSet);
        } catch (IOException e) {
            log.error("建议词自动补全异常", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 处理关键字补全方法结果
     *
     * @param searchResponse 关键字补全请求结果
     * @return albumTitleSet 补全标题集合
     */
    @Override
    public List<String> handleResponse4CompleteSuggest(SearchResponse<SuggestIndex> searchResponse,
                                                       String suggester) {
        List<String> albumTitleList = new ArrayList<>();
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(suggester);
        if (CollUtil.isNotEmpty(suggestionList)) {
            for (Suggestion<SuggestIndex> suggestion : suggestionList) {
                for (CompletionSuggestOption<SuggestIndex> option : suggestion.completion().options()) {
                    String title = option.source().getTitle();
                    albumTitleList.add(title);
                }
            }

        }
        return albumTitleList;
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 更新所有分类下排行榜
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //检索ES获取榜单top20
            //获取一级分类ID集合
            List<BaseCategory1> baseCategory1List = albumFeignClient.findAllCategory1().getData();
            Assert.notEmpty(baseCategory1List, "一级分类表为空");

            for (BaseCategory1 category1  : baseCategory1List) {
                Long category1Id = category1.getId();
                String idKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
                BoundHashOperations<String, String, List<AlbumInfoIndex>> boundHashOperations = redisTemplate.boundHashOps(idKey);
                String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
                for (String rankingDimension : rankingDimensionArray) {
                    SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
                    SearchRequest searchRequest = searchRequestBuilder.index("albuminfo")
                            .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
                            .sort(s->s.field(f->f.field(rankingDimension).order(SortOrder.Desc)))
                            .size(20)
                            .source(s -> s.filter(f -> f.excludes("attributeValueIndexList",
                                    "hotScore",
                                    "commentStatNum",
                                    "buyStatNum",
                                    "subscribeStatNum",
                                    "announcerName"))).build();
                    SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);

                    List<AlbumInfoIndex> albumInfoIndexList = searchResponse.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(albumInfoIndexList)) {
                        boundHashOperations.put(rankingDimension, albumInfoIndexList);
                    }
                }
            }
        } catch (Exception e) {
            log.error("更新Redis榜单失败");
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取排行榜
     * @param category1Id
     * @param dimension
     * */
    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {
        String idKey = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        String field = dimension;
        BoundHashOperations<String, String, List<AlbumInfoIndex>> boundHashOperations = redisTemplate.boundHashOps(idKey);
        if (boundHashOperations.hasKey(field)){
            List<AlbumInfoIndex> albumInfoIndexList = boundHashOperations.get(field);
            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndexList.stream().map(albumInfoIndex -> BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class)).collect(Collectors.toList());
            return albumInfoIndexVoList;
        }
        return null;
    }

    /**
     * 上架专辑信息: 保存至索引库
     * -并不需要做幂等性处理,重复提交会覆盖前值
     *
     * @param albumId 专辑ID
     * @todo: 异步处理
     */
    @Override
    public void upperAlbum(Long albumId) {
        //1 创建索引文档对象 AlbumInfoIndex
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();

        //使用线程池和异步编排优化Feign调用
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //2 调用专辑服务,获取专辑信息及标签信息
            //2.1 获取封装专辑信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "搜索专辑{}不存在", albumId);
            BeanUtil.copyProperties(albumInfo, albumInfoIndex);
            //2.2 获取封装专辑标签列表
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            if (CollUtil.isNotEmpty(albumAttributeValueVoList)) {
                List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue ->
                        BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class)
                ).collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }
            return albumInfo;
        }, threadPoolTaskExecutor);

        CompletableFuture<Void> baseCategoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //3 调用专辑分类服务,获取专辑分类信息
            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
            if (albumInfo == null) {
                log.error("专辑信息为空");
            }
            Assert.notNull(baseCategoryView, "搜索专辑分类{}不存在", albumInfo.getCategory3Id());
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            //4 调用用户服务,获取主播相关信息
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId()).getData();
            if (userInfoVo == null) {
                log.error("用户信息为空");
            }
            Assert.notNull(userInfoVo, "搜索用户{}不存在", albumInfo.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        }, threadPoolTaskExecutor);

        CompletableFuture<Void> statNumCompletableFuture = albumInfoCompletableFuture.runAsync(() -> {
            //5 todo: 封装统计数据(暂时封装随机值)
            int playStatNum = RandomUtil.randomInt(0, 1000);
            int subscribeStatNum = RandomUtil.randomInt(0, 50);
            int buyStatNum = RandomUtil.randomInt(0, 50);
            int commentStatNum = RandomUtil.randomInt(0, 50);
            albumInfoIndex.setPlayStatNum(playStatNum);
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            albumInfoIndex.setBuyStatNum(buyStatNum);
            albumInfoIndex.setCommentStatNum(commentStatNum);
            //加权计算专辑热度
            BigDecimal hotScore = new BigDecimal(playStatNum).multiply(new BigDecimal(0.1))
                    .add(new BigDecimal(subscribeStatNum).multiply(new BigDecimal(0.2)))
                    .add(new BigDecimal(buyStatNum).multiply(new BigDecimal(0.3)))
                    .add(new BigDecimal(commentStatNum).multiply(new BigDecimal(0.4)));
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        }, threadPoolTaskExecutor);

        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                baseCategoryViewCompletableFuture,
                statNumCompletableFuture
        ).orTimeout(10, TimeUnit.SECONDS).join();
        //6 保存专辑索引库文档对象
        albumInfoIndexRepository.save(albumInfoIndex);
        //7 保存专辑标题索引至索引库
        this.saveSuggestIndex(albumInfoIndex);
    }

    /**
     * 下架专辑信息: 删除索引库文档和标题库文档
     *
     * @param albumId 专辑ID
     */
    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
        suggestIndexRepository.deleteById(albumId.toString());
    }
}
